Skip to content
标签
spring
字数
2527 字
阅读时间
12 分钟

一、概述

Spring 容器和 SpringMVC 容器是父子容器.SpringMVC 容器中能够调用 Spring 容器的所有内容

1.1 运行原理

如果在 web.xml 中设置 DispatcherServlet 的<url-pattern>为/时,当用户发 起 请 求 , 请 求 一 个 控 制 器 , 首 先 会 执 行 DispatcherServlet. 由DispatcherServlet 调 用 HandlerMapping 的DefaultAnnotationHandlerMapping 解 析 URL, 解 析 后 调 用HandlerAdatper 组 件 的AnnotationMethodHandlerAdapter 调 用Controller 中的HandlerMethod.当 HandlerMethod 执行完成后会返回View,会被 ViewResovler 进行视图解析,解析后调用 jsp 对应的.class 文件并运行,最终把运行.class 文件的结果响应给客户端

二、核心知识

2.1 配置前端控制器

xml
<!-- Web.xml -->
<servlet>
    <servlet-name>jqk</servlet-name>    <!-- 自定义名字,与mapping对应 -->
    <servlet-class>org.springframework.web.servlet.DispatcherServlet<servlet-class>  <!-- 类的路径 -->
    <init-param>
        <param-name>contextConfigLocation</param-name>    <!-- 定义参数,配值文件路径属性 -->
        <param-value>classpath:springmvc.xml</param-value>   <!-- 配置文件的路径 -->
    </init-param>
    <load-on-startup>1</load-on-startup>   <!-- 启动时加载 --> 
</servlet>
<servlet-mapping>
    <servlet-name>jqk</servlet-name>
    <url-pattern>/</url-pattern>        <!-- 拦截的路径,/表示除jsp,其余全部拦截 -->
</servlet-mapping>

2.2 springmvc配置

xml
<!-- springmvc.xml -->
<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:mvc="http://www.springframework.org/schema/mvc" 
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context 
                           http://www.springframework.org/schema/context/spring-context.xsd 
                           <!-- 引入 xmlns:mvc 命名空间 --> 
                           http://www.springframework.org/schema/mvc 
                           http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 扫描注解 --> 
    <context:component-scan base-package="com.bjsxt.controller"></context:component-scan>
    <!-- 注解驱动 --> 
    <!-- org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandler Mapping --> 
    <!-- org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerA dapter -->
    <!-- 配置视图解析器 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"></property> <!-- 在转发前默认添加的前缀 --> 
        <property name="suffix" value=".jsp"></property> <!-- 默认添加的后缀 --> 
    </bean>
    <mvc:annotation-driven></mvc:annotation-driven> 
    <!-- 静态资源 --> 
    <mvc:resources location="/js/" mapping="/js/**"></mvc:resources> 
    <mvc:resources location="/css/" mapping="/css/**"></mvc:resources>
    <mvc:resources location="/images/" mapping="/images/**"></mvc:resources> 
</beans>
<!-- 如果希望不执行自定义视图解析器,在方法返回值前面添加forward:或 redirect: -->

2.3 添加字符编码过滤器

xml
<!-- Web.xml -->
<filter>
    <filter-name>encoding</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>   <!-- 过滤器的位置 --> 
    <init-param>
        <param-name>encoding</param-name>   <!-- 设值字符编码的属性名 --> 
        <param-value>utf-8</param-value>    <!-- 设置字符编码格式 -->
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encoding</filter-name>
    <url-pattern>/*</url-pattern>     <!-- 拦截路径,全部拦截 -->
</filter-mappin>

2.4 参数接收

java
@Controller
public class DemoController {
    //基本数据类型
    @RequestMapping("demo") 
    //把内容写到方法(HandlerMethod)参数中,SpringMVC 只要有这个内 容,注入内容
    //默认保证参数名称和请求中传递的参数名相同 value="参数名"
    //如果请求参数名和方法参数名不对应使用@RequestParam()赋值
    //如果方法参数是基本数据类型(不是封装类)可以通过 @RequestParam 设置默认值 defaultValue="默认值"
    //如果强制要求必须有某个参数required=true
    // @CookieValue(value="JSESSIONID",required=false) 可以获取cookie的值
    public String demo(@RequestParam(value="name1",required=true) String name,@RequestParam(defaultValue="2") int age){
        System.out.println("执行 demo"+" "+name+" "+age);
        return "main.jsp"; 
    } 
    
    // 对象类型
    @RequestMapping("demo4") 
    //请求参数名和对象中属性名对应(get/set 方法)
    // @RequestBody()可接收json格式数据
    public String demo4(People peo){
        return "main.jsp";
    }
    
    //在@RequestMapping 中一定要和请求格式对应
	//{名称} 中名称自定义名称
	//@PathVariable 获取@RequestMapping 中内容,默认按照 方法参数名称去寻找.
	@RequestMapping("demo8/{id1}/{name}")
	public String demo8(@PathVariable String name,@PathVariable("id1") int age){
    	System.out.println(name +" "+age);
    	return "/main.jsp"; 
	}
    
}

2.5 数据回显

数据执行出错后将原数据返回原页面 mvc会将对象属性默认赋值到请求作用域中,在jsp中EL表达式调用 其余可通过model调用

2.6 跳转方式

默认的调转方式是请求转发。

java
// 重定向
response.sendRedirect("testRetrunString");
// 转发
request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request, response);

2.7 常用注解

java
//在方法上添加@ResponseBody(发放不进行转发到视图解析器,直接返回数据) 
// 如果返回值满足 key-value 形式(对象或 map)
// 默认把响应头设置为 application/json;charset=utf-8  然后以输出流的形式响应给客户端
// 底层使用 Jackson 进行 json 转换,在项目中一定要导入 jackson 的 jar
//如果返回值不满足 key-value  把相应头设置为 text/html 然后以输出流的形式响应给客户端
// 如果返回值包含中文,出现中文乱码,在RequestMapping中设置charset的值
//  @RequestMapping(value="demo12",produces="text/html; charset=utf-8")
@ResponseBody
  
// 获取指定请求头的值  value:请求头的名称
@RequestHeader(value="Accept")

// 出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。
// 出现在参数上,获取指定的数据给参数赋值。 
//  属性:value:用于获取数据的key。
@ModelAttribute

// 在类上标识,用于多次执行控制器方法间的参数共享。
// 属性: value:用于指定存入的属性名称  type:用于指定存入的数据类型。
@SessionAttributes

2.8 作用域传值

java
@RequestMapping("demo1") 
public String demo1(HttpServletRequest abc,HttpSession sessionParam){ 
    //request 作用域 
    abc.setAttribute("req", "req 的值"); 
    //session 作用域 
    HttpSession session = abc.getSession();
    session.setAttribute("session", "session 的值"); 
    sessionParam.setAttribute("sessionParam", "sessionParam 的值"); 
    //appliaction 作用域 
    ServletContext application = abc.getServletContext(); 
    application.setAttribute("application","application 的值"); 
    return "/index.jsp"; 
}

//把 map 中内容放在 request 作用域中
//spring 会对 map 集合通过 BindingAwareModelMap 进行实例化
@RequestMapping("demo2") 
public String demo2(Map<String,Object> map){
    System.out.println(map.getClass()); 
    map.put("map","map 的值"); 
    return "/index.jsp";
}

//把内容最终放入到 request 作用域中.
@RequestMapping("demo3")
public String demo3(Model model){
    model.addAttribute("model", "model 的值"); 
    return "/index.jsp"; 
}

@RequestMapping("demo4")
public ModelAndView demo4(){
    //参数,跳转视图 
    ModelAndView mav = new ModelAndView("/index.jsp"); 
    mav.addObject("mav", "mav 的值"); 
    return mav; 
}

2.9 自定义拦截器

java
// 发送请求时被拦截器拦截,在控制器的前后添加额外功能 针对点是控制器方法.(对 Controller)
// 跟 AOP 区分开.AOP 在特定方法前后扩充(对 ServiceImpl) 
// 拦截器只能拦截器 Controller
// Filter 可以拦截任何请求

// 创建拦截器
public class DemoInterceptor implements HandlerInterceptor { 
    //在进入控制器之前执行 
    //如果返回值为 false,阻止进入控制器 
    //控制代码 
    @Override
    public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
        System.out.println("arg2:"+arg2); 
        System.out.println("preHandle"); 
        return true; 
    }
    //控制器执行完成,进入到 jsp 之前执行. 
    //日志记录.
    //敏感词语过滤
    @Override 
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
        System.out.println("往"+arg3.getViewName()+"跳转 "); 
        System.out.println("model 的值 "+arg3.getModel().get("model"));
        String word = arg3.getModel().get("model").toString();
        String newWord = word.replace("祖国", "**"); 
        arg3.getModel().put("model", newWord);
        // arg3.getModel().put("model", "修改后的内容"); 
        System.out.println("postHandle"); 
    }
    //jsp 执行完成后执行 
    //记录执行过程中出现的异常. 
    //可以把异常记录到日志中 
    @Override 
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {
        System.out.println("afterCompletion"+arg3.getMessag e()); 
    } 
}

配置拦截器

xml
<!-- 配置拦截器 -->
<mvc:interceptors> 
    <bean class="com.bjsxt.interceptor.DemoInterceptor"></bean>
    <!-- 不配置interceptor为拦截所有控制器,配置为拦截配置的url -->
    <mvc:interceptor> 
        <mvc:mapping path="/demo"/> 
        <mvc:mapping path="/demo1"/> 
        <mvc:mapping path="/demo2"/> 
        <bean class="com.bjsxt.interceptor.DemoInterceptor"></bean> 
    </mvc:interceptor>
</mvc:interceptors>

2.10 拦截器栈

  • 多个拦截器同时生效时,组成了拦截器栈
  • 顺序:先进后出
  • 执行顺序和在 springmvc.xml 中配置顺序有关
  • 设置先配置拦截器 A 在配置拦截器 B 执行顺序为
  • preHandle(A) --> preHandle(B) --> 控制器方法 --> postHandle(B) --> postHanle(A) --> JSP --> afterCompletion(B) --> afterCompletion(A)

类型转换,以Date转换为例

方式一

xml
<!-- 
在 springmvc.xml 中配置,代码中不需要做任何修改
必须额外导入 joda-time.jar
时间类型 java.sql.Date
 -->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
<bean id="conversionService" class="org.springframework.format.support.Formattin gConversionServiceFactoryBean"> 
    <property name="registerDefaultFormatters" value="false" /> <property name="formatters"> 
    <set> 
        <bean class="org.springframework.format.number.NumberForm atAnnotationFormatterFactory" /> 
    </set> 
    </property>
    <property name="formatterRegistrars">
        <set> 
            <bean class="org.springframework.format.datetime.joda.Jod aTimeFormatterRegistrar">
                <property name="dateFormatter"> 
                    <bean class="org.springframework.format.datetime.joda.Dat eTimeFormatterFactoryBean"> 
                        <property name="pattern" value="yyyy-MM-dd" /> 
                    </bean>
                </property> 
            </bean>
        </set> 
    </property> 
</bean>

方式二

java
//使 用 注 解 . 在 需 要 转 换 的 参 数 或 实 体 类 属 性 上 添 加 
// Date 是 java.util.Date
//@DateTimeFormatter(pattern=”表达式”)
//使用 Date 参数接收
@RequestMapping("demo") 
public String demo(@DateTimeFormat(pattern="yyyy-MM-dd") Date time){
    System.out.println(time); 
    return "abc.jsp"; 
}

//在实体类中,通过在属性上添加注解,控制层直接通过实体类接收
@DateTimeFormat(pattern="yyyy/MM/dd") 
private Date time;

方式三

java
/**
* 自定义类型转换器
* 把字符串转换成日期的转换器
* @author rt
*/
public class StringToDateConverter implements Converter<String, Date>{
    /**
    * 进行类型转换的方法
    */
	public Date convert(String source) {
        // 判断
        if(source == null) {
        	throw new RuntimeException("参数不能为空");
        }
        try {
			DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
			// 解析字符串
			Date date = df.parse(source);
			return date;
		} catch (Exception e) {
			throw new RuntimeException("类型转换错误");
		}
	}
}

配置

xml
<!-- 在springmvc.xml配置文件中配置 -->
<!-- 注册自定义类型转换器 -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
	<property name="converters">
		<set>
			<bean class="cn.itcast.utils.StringToDateConverter"/>
		</set>
	</property>
</bean>
<!-- 开启Spring对MVC注解的支持 -->
<mvc:annotation-driven conversion-service="conversionService"/>

2.11 数据校验

依赖

xml
// 添加jar包
hibernate-validator-4.3.0
jboss-logging-3.1.0
validation-api-1.0.0

配置

xml
	<!-- spring.xml -->
	<!-- 校验器 -->
	<bean id="validator"	class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
		<!-- hibernate校验器-->
		<property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
		<!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用classpath下的ValidationMessages.properties -->
		<property name="validationMessageSource" ref="messageSource" />
	</bean>
<!-- 校验错误信息配置文件 -->
	<bean id="messageSource"
		class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
		<!-- 资源文件名-->
		<property name="basenames">   
       	 <list>    
            <value>classpath:CustomValidationMessages</value> 
       	 </list>   
    	</property>
		<!-- 资源文件编码格式 -->
		<property name="fileEncodings" value="utf-8" />
		<!-- 对资源文件内容缓存时间,单位秒 -->
		<property name="cacheSeconds" value="120" />
	</bean>

注册校验器

xml
<!-- 注册校验器 -->
<mvc:annotation-driven conversion-service="conversionService" validator = "validator"></mvc:annotation-driven>

使用

java
// 使用
//在属性上通过注解校验数据长度和是否为空
//错误信息是在CustomValidationMessages.properties中配置
@Size(min=1,max=30,message="{items.name}")
privte String name;

@NotNull(message="{items.name}")
private Date createtime;

// 在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult bindingResult接收校验出错信息
public String testValid(@Validated TestModel testModel,BindingResult bindingResult);

2.12 全局异常处理

java
// 实现HandlerExceptionResolver  注册类
@Override
	public ModelAndView resolveException(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex) {
		//处理异常
		CustomException customException = null;
		if(ex instanceof CustomException){
			customException = (CustomException)ex;
		}else{
			customException = new CustomException("未知错误");
		}
		
		//错误信息
		String message = customException.getMessage();
		
		
		ModelAndView modelAndView = new ModelAndView();
		
		//将错误信息传到页面
		modelAndView.addObject("message", message);
		
		//指向错误页面
		modelAndView.setViewName("error");

		
		return modelAndView;
	}